#include "arraylist.h"
#include <stdlib.h>
#include <errno.h>

/**
 * Creates a new arraylist with an initial capacity.
 * Returns NULL on failure.
 */
arraylist_t *arraylist_create(unsigned int capacity)
{
	arraylist_t *array = malloc(sizeof(arraylist_t));
	if (array != NULL)
	{
	  array->count = 0;
	  array->capacity = capacity;
	  array->data = malloc(sizeof(void*) * capacity);
	  if (array->data == NULL)
	  {
		errno = ENOMEM;
		free(array);
		return NULL;
	  }
	}
	else
	{
		errno = ENOMEM;
	}

	return array;
}

/**
 * Destroys an arraylist and optionally its contents.
 */
void arraylist_destroy(arraylist_t *array, int freeContents)
{
	// Free the contents, the array, then the structure.
	arraylist_clear(array, freeContents);

	free(array->data);
	free(array);
}

/**
 * Resizes an arraylist to a larger capacity.
 * Returns nonzero on success.
 */
int arraylist_resize(arraylist_t *array, unsigned int new_capacity)
{
	array->data = realloc(array->data, sizeof(void*) * new_capacity);
	if (array->data == NULL)
	{
		errno = ENOMEM;
		free(array);
		return 0;
	}

	array->capacity = new_capacity;
	return 1;
}

/**
 * Adds data to the end of an arraylist.
 * Returns nonzero on success.
 */
int arraylist_add(arraylist_t *array, void *data)
{
	// If the arraylist is out of space.
	if (array->count >= array->capacity)
	{
		// Double the capacity.
		if (arraylist_resize(array, array->capacity * 2) != 0)
		{
			return 0;
		}
	}

	// Set the size + 1th item in the array to the new data.
	array->data[array->count] = data;
	++array->count;
	return 1;
}

/**
 * Returns the element at index in the array.
 */
void *arraylist_get(arraylist_t *array, unsigned int index)
{
	return array->data[index];
}

/**
 * Removes and returns the element at index from the array.
 */
void *arraylist_remove(arraylist_t *array, unsigned int index)
{
	void *result = array->data[index];

	unsigned int i;
	for (i = index + 1; i < array->count; ++i)
	{
		array->data[i - 1] = array->data[i];
	}

	--array->count;
	return result;
}

/**
 * Removes all elements from the arraylist and optionally frees them.
 */
void arraylist_clear(arraylist_t *array, int freeContents)
{
	if (freeContents)
	{
		unsigned int i;
		for (i = 0; i < array->count; ++i)
		{
			free(array->data[i]);
		}
	}

	array->count = 0;
}

/**
 * Inserts an item at index.
 * Returns nonzero on success.
 */
int arraylist_insert(arraylist_t *array, void *data, unsigned int index)
{
	if (array->count >= array->capacity)
	{
		if (arraylist_resize(array, array->capacity * 2) != 0)
		{
			return 0;
		}
	}

	unsigned int i;
	for (i = array->count; i > index; --i)
	{
		array->data[i] = array->data[i - 1];
	}

	array->data[index] = data;
	return 1;
}

/**
 * Sets the data for the item at index and returns the old data.
 */
void *arraylist_set(arraylist_t *array, void *data, unsigned int index)
{
	void *old = array->data[index];
	array->data[index] = data;
	return old;
}

/**
 * Sets index to that of the first element in the list with the given value.
 * Returns nonzero if the item was found, 0 if not (index is unchanged).
 */
int arraylist_find(arraylist_t *array, void *data, unsigned int *index)
{
	int n;
	for (n = 0; n < array->count; ++n)
	{
		if (array->data[n] == data)
		{
			*index = n;
			return 1;
		}
	}

	return 0;
}

/**
 * Sets index to that of the first element in the list that matches a given value.
 * A match is determined by the result of calling the comparer function for each item in the list.
 * The comparer should return 0 if the two items do not match and nonzero for a match.
 * Returns nonzero if the item was found, 0 if not (index is unchanged).
 */
int arraylist_search(arraylist_t *array, void *data, unsigned int *index, int (*comparer)(void *data1, void *data2))
{
	int n;
	for (n = 0; n < array->count; ++n)
	{
		if (comparer(array->data[n], data))
		{
			*index = n;
			return 1;
		}
	}

	return 0;
}

